#include "httpParse.h"

static int fileWrittenBytes = 0;
bool uploadFlag = false;
/*@brief:
 *@para:
 *
 *@return value:
 */
void httpSendErrorPage(struct connection *conn, int errorCode, char *reason){
    if(!reason){
        reason = httpStatusMessage(errorCode);
    }
    httpSendResponseLine(conn, errorCode, reason);
    //构建头部信息 call function;
    conn->state = SEND_RESPONSE;
    return;
}


/*
 *@brief: http服务的初始化
 *@para: struct httpServerCtx **http:
 *		 struct serverConfigOpt *opt:
 *
 *@return value:
 */
int httpServerInit(struct httpServerCtx **http, struct serverConfigOpt *opt){	
    struct httpServerCtx *tmp = *http;
    if(!opt){
        fprintf(stderr, "[error]get unavaliable args in file:[%s] func:[%s] line:[%d]\n",__FILE__, __func__,__LINE__);
        return RET_ERROR;
    }
    if(!tmp){
        tmp = (struct httpServerCtx *)malloc(sizeof(struct httpServerCtx));
        if(!tmp){
            fprintf(stderr, "[error]%s in file:[%s] func:[%s] line:[%d]\n",strerror(errno), __FILE__, __func__,__LINE__);
            return RET_ERROR;
        }
    }
    tmp->rootDirPath = strdup(opt->rootDirPath);
    tmp->indexHtmlPath = strdup(opt->indexHtmlPath);
    tmp->enable_directory_listing = strdup(opt->enable_directory_listing);
    tmp->gzipOnFlag = opt->gzipOnFlag;  //gzip 开关;
    *http = tmp;
    return RET_OK;
}

/*@brief:
 *@para:struct httpServerCtx *http:
 *
 *@return value:
 */
int httpServerDeinit(struct httpServerCtx *http){
    if(http->rootDirPath){
        free(http->rootDirPath);
        http->rootDirPath = NULL;
    }
    if(http->indexHtmlPath){
        free(http->indexHtmlPath);
        http->indexHtmlPath = NULL;
    }
    if(http->enable_directory_listing){
        free(http->enable_directory_listing);
        http->enable_directory_listing = NULL;
    }
    if(http){
        free(http);
        http = NULL;
    }
    return RET_OK;
}

/*@brief:处理请求;
 *@para:
 *
 *@return value:
 */
int httpParseRequest(struct connection *conn, struct httpMessage *msg, bool isRequest){
    /*
     *@step 获取报文的长度;
     */
    char *start = (char *)conn->recvbuf;
    int iRet = RET_ERROR;
    int requestLen = RET_ERROR;
    char *end, *queryString;
    if(uploadFlag == true){
        requestLen = strlen(start);
        memset(conn->recvbuf, 0, conn->bufSize);
        fileWrittenBytes += requestLen;
        printf("[INFO]fileWrittenBytes:%d\n", fileWrittenBytes);
    }else{
        requestLen = httpGetRequestLen(conn); //请求行+headers; 不包含content的长度;
        if( requestLen <= 0){
            return iRet;
        }
        msg->message.str = start;
        msg->body.str = start + requestLen;
        msg->message.len = msg->body.len = (size_t)~0;
        end = start + requestLen;
       /*
        *@去除报文头部的空格;
        */
        while(start < end && isspace(*(unsigned char *)start))  start++;
       /*
        * @判断是请求或者是响应 解析请求行和状态行;
        */
        if(isRequest){
            start = httpSkip(start, end, " ", &msg->method);
            start = httpSkip(start, end, " ", &msg->uri);
            start = httpSkip(start, end, " ", &msg->version);

        /*
         *@检查是不是表单提交带参数了;
         */
            if((queryString = ( char *)memchr(msg->uri.str, '?', msg->uri.len)) != NULL){
                msg->queryString.str = queryString + 1;
                msg->queryString.len = &(msg->queryString.str[msg->uri.len]) - queryString - 1;
                msg->uri.len = queryString - msg->uri.str;
            }
        }else{
            /*
             *@请求响应状态行;
             */
            start = httpSkip(start, end, " ", &msg->version);
            if( end - start < 4 || start[3] != ' '){
                return RET_ERROR;
            }
            msg->responseCode = atoi(start);
            /*
             *@响应的状态码的取值范围
             */
            if( msg->responseCode < 100 || msg->responseCode >= 600){
                return RET_ERROR;
            }
            start += 4;
            start = httpSkip(start, end, "\r\n", &msg->responseStatusMsg); //获取状态码信息;
        }
        /*
         *@解析报文的头部字段
         */
        start = httpParseRequestHeader(start, end, requestLen, msg);

        /*@特殊场景的报文长度设置
         *@不是PUT或者POST的方法的请求 正文体长度设置为0
         */
        if(msg->body.len == (size_t)~0 && isRequest && strcasecmp(msg->method.str, "PUT")!=0 && strcasecmp(msg->method.str, "POST")!=0){
            msg->body.len = 0;
            msg->message.len = requestLen;
        }
        if((strncmp(msg->uri.str, "/upload", msg->uri.len) == 0) && (strncmp(msg->method.str, "POST", msg->method.len)== 0)){
            memset(conn->recvbuf, 0, conn->bufSize);
            uploadFlag = true;
        }
        if(strncmp(msg->uri.str, "/favicon.ico", msg->uri.len) == 0){
            return RET_OK;
        }   
        if((strncmp(msg->uri.str,"/", msg->uri.len) == 0) && (strncmp(msg->method.str, "GET", msg->method.len) == 0)){
            return  RET_OK;
        }
    } 
    return requestLen;
}

/*
 *@brief:解析请求行的头部
 *@para: 
 *
 *
 *@return value:
 */
char * httpParseRequestHeader(char *start, char *end, int requestLen, struct httpMessage *msg){
    int i = 0;
    while( i < (int)ARRAY_SIZE(msg->headerNames) - 1){
        struct dynString *keyName = &(msg->headerNames[i]), *valueName = &(msg->headerValues[i]);
        start = httpSkip(start, end, ": ", keyName);
        start = httpSkip(start, end, "\r\n", valueName);
        /*
         *@
         */
        while(valueName->len  >  0 && valueName->str[valueName->len -1] == ' '){
            valueName->len--;
        }
        /*
         *@
         */
        if (keyName->len != 0 && valueName->len == 0){
            continue;
        }

        if(keyName->len == 0 || valueName->len == 0){
            keyName->str =  valueName->str = NULL;
            keyName->len = valueName->len = 0;
            break;
        }

        if(!strncasecmp(keyName->str, "Content-Length", 14)){
            /*
             *@
             */
            msg->body.len = (size_t)to64(valueName->str);
            msg->message.len = requestLen + msg->body.len;
        }
        i++;
    }
    /*
     *@
     */
    return start;
}


/*@brief:获取请求的长度
 *@para:struct connection *conn:网络连接 
 *
 *@return value:
 */
int httpGetRequestLen(struct connection *conn){
    int iRet = RET_ERROR;
    if(!conn){
        fprintf(stderr, "[error]get unavaliable args in [file]:%s [func]:%s [line]:%d\n", __FILE__,__func__, __LINE__);
        return iRet;
    }
    char *buf = (char *)conn->recvbuf;
    int  currentLen = strlen(buf);
    int i = 0;
    for( i = 0; i < currentLen; i++){
        if(!isprint(buf[i]) && buf[i] != '\r' && buf[i] != '\n' && buf[i] < 128){
            return RET_ERROR;	
        }else if(buf[i] == '\n' && i + 1 < currentLen && buf[i + 1] == '\n'){
            return i + 2;
        }else if(buf[i] == '\n' && i + 2 < currentLen && buf[i + 1] == '\r' && buf[i + 2] == '\n'){
            return i + 3;
        }
    }
    return RET_OK;
}


/*
 *@brief:处理Http的请求
 *@para: struct connection *conn:网络连接 
 *		 struct httpMessage *msg:
 *		 struct httpServerCtx *httpctx:
 *@return value:
 */
int handleHttpRequest(struct connection *conn, struct httpMessage *msg, struct httpServerCtx *httpctx){
    int iRet = RET_ERROR;
    char *path = NULL;
    if(!conn || !msg || !httpctx){
        fprintf(stderr, "[error]get unavailable args in [file]:%s [func]:%s [line]:%d\n",__FILE__,__func__,__LINE__);
        return iRet;
    }
    struct dynString pathInfo;
    /*
     *@step1:
     */
    path = strdup(httpctx->indexHtmlPath);
    pathInfo.str = strdup(msg->uri.str + 1);
    pathInfo.len = 0;
    /*
     *@step3:
     */
    iRet = httpSendHttpFile(conn, path, &pathInfo, msg, httpctx);

    if(path){
        free(path);
        path = NULL;
    }
    /*
     *@解决内存泄漏问题;
     */
    if(pathInfo.str){
        free(pathInfo.str);
        pathInfo.str = NULL;
    }
    return RET_OK;
}

/*
 *@brief:
 *@para:struct connection *conn:
 *      char *path:
 *		struct dynString *pathInfo:
 *		struct httpMessage *msg:
 *		struct httpServerCtx *httpctx;
 *
 */
int httpSendHttpFile(struct connection *conn, char *path, struct dynString *pathInfo, struct httpMessage *msg, struct httpServerCtx *httpctx){

    int iRet = RET_ERROR;
    if(!conn||!path||!pathInfo||!msg||!httpctx){
        fprintf(stderr, "[error]get unavailable args in [file]:%s [func]:%s [line]:%d\n", __FILE__,__func__,__LINE__);
        return iRet;
    }
    int exists = RET_ERROR;
    char *indexFilePath =  NULL;
    struct stat  indexfile;

    exists = stat(path, &indexfile);

    /*
     *@indexhtml 不存在 发错误页面;
     */
    if(exists != RET_OK){
        httpSendErrorPage(conn, 403, "indexhtml is not exist!"); 
        return RET_ERROR;
    }

    conn->gzipOn = httpctx->gzipOnFlag;

    if(conn->gzipOn == true){
        gzipInit(&(conn->stream));
    }

    //	isDirectory = exists && S_ISDIR(indexfile.st_mode);
    /*
     *@匹配不同的方法执行
     */

    struct dynString extraHeaders;
    memset(&extraHeaders, 0, sizeof(struct dynString));
    /*
     *@extraHeaders 这个怎么追加;
     */
    iRet = httpServeFile(conn, indexFilePath ? indexFilePath : path, msg , extraHeaders);
    if(indexFilePath){
        free(indexFilePath);
        indexFilePath = NULL;
    }

    return RET_OK;
}

/*
 *@brief:
 *@para:struct connection *conn:
 *		char *path:
 *		struct httpMessage *msg:
 *		struct dynString extraHeaders:
 */
int httpServeFile(struct connection *conn,char *path, struct httpMessage *msg, struct dynString  extraHeaders){
#define GMT_TIME_SIZE 50
    struct stat indexfileSt;
    int iRet = RET_ERROR;
    if(!conn || !path || !msg){
        fprintf(stderr, "[error]get unavailable  args in [file]:%s [func]:%s [line]:%d\n", __FILE__,__func__,__LINE__);
        return iRet;
    }
    int errorCode = -1;
    char etag[GMT_TIME_SIZE] = { '\0' };
    char currentTime[GMT_TIME_SIZE] = { '\0' };
    char lastModifiTime[GMT_TIME_SIZE] = { '\0' };
    int statusCode = 200;
    bool keepAliveFlag = false;
    struct httpOpt *opt = &(conn->httpopt);
    /*
     *@brief:判断index是否存在并且打开它.
     */
    if(stat(path, &indexfileSt) != 0 || (opt->fp= fopen(path, "rb")) == NULL){
        switch(errno){
        case EACCES:
            errorCode = 403;
            break;
        case ENOENT:
            errorCode = 404;
            break;
        default:
            errorCode = 500;
            break;
        }
        httpSendErrorPage(conn, errorCode, "Open failed!");
    }

    opt->contentLen = indexfileSt.st_size;  //获取请求文件的大小;
    httpConstructEtag(etag, sizeof(etag), &indexfileSt);
    /*
     *@打上GMT 时间戳;
     */
    time_t time = httpGetLocalTime();
    httpGmtTimeString(currentTime, sizeof(currentTime), &time);
    httpGmtTimeString(lastModifiTime, sizeof(lastModifiTime), &indexfileSt.st_mtime);
    iRet = httpSendResponseLine(conn, statusCode, extraHeaders.str);

    /*
     *@step1:
     */
    struct dynString MiMeType = { "text/html", 9 };

    conn->currDataLen += snprintf((char *)conn->sendbuf + conn->currDataLen, conn->bufSize - conn->currDataLen, "Date: %s\r\n"
                                  "Last-Modified: %s\r\n"
                                  "Content-Type: %.*s\r\n"
                                  "Contention: %s\r\n"
                                  "Content-Length: %ld\r\n"
                                  "Etag: %s\r\n",
                                  currentTime, lastModifiTime, (int)MiMeType.len, MiMeType.str, (keepAliveFlag == true ? "keep-alive" : "close"), opt->contentLen, etag);
    /*
     *@如果开了gzip的开关;
     */
    if(conn->gzipOn == true){
        conn->currDataLen += snprintf((char *)conn->sendbuf + conn->currDataLen, conn->bufSize - conn->currDataLen, "Content-Encoding: gzip\r\n\r\n");
    }else{
        conn->currDataLen += snprintf((char *)conn->sendbuf + conn->currDataLen, conn->bufSize - conn->currDataLen, "\r\n");
    }
    /*
     *@step2:
     */
    iRet = httpTransferFileData(conn);
    return iRet;
}

/*@brief:缓存文件到缓存等待发送;
 *@para:struct connection *conn:
 *		char *buf:
 *		size_t buffLen:
 *
 */
int bufferedFileData(struct connection *conn, char *buf,  size_t buffLen){
#define NEW_DATA_LEN_RESIZE  2
    int iRet = RET_ERROR;
    if(!conn||!buf){
        fprintf(stderr, "[error]get unavailable args in [func]:%s [file]:%s [line]:%d\n",__func__, __FILE__,__LINE__);
        return iRet;
    }
    unsigned char *newaddr = NULL;
    size_t newDataLen = 0;
    size_t leftLen = conn->bufSize - conn->currDataLen;
    if(leftLen > buffLen){
        //够了, 就直接拷贝下;
        memmove(conn->sendbuf + conn->currDataLen + buffLen, conn->sendbuf + conn->currDataLen, 0 );
        /*
         *@call gzipData
         */
        if(conn->gzipOn == true){
            gzipData(&(conn->stream), conn->sendbuf + conn->currDataLen, leftLen , (unsigned char *)buf,  buffLen);
        }else{
            memcpy(conn->sendbuf + conn->currDataLen, buf, buffLen);
        }
    }else{
        //内存不够;
        newDataLen = ( conn->currDataLen + buffLen) * NEW_DATA_LEN_RESIZE;
        newaddr = (unsigned char *)realloc(conn->sendbuf, newDataLen *sizeof(unsigned char));
        if(!newaddr){
            fprintf(stderr, "[error]malloc memory operation failed! in [file]:%s [func]:%s [line]:%d\n", __func__, __FILE__,__LINE__);
            return RET_ERROR;
        }
        memmove(newaddr + conn->currDataLen + buffLen, newaddr + conn->currDataLen, 0 );
        if(conn->gzipOn == true){
            gzipData(&(conn->stream), newaddr + conn->currDataLen, newDataLen, (unsigned char *)buf,  buffLen);
        }else{
            memcpy(newaddr + conn->currDataLen, buf, buffLen);
        }
        conn->sendbuf = ( newaddr == conn->sendbuf) ? newaddr : conn->sendbuf;
        conn->bufSize =newDataLen; 
    }
    conn->currDataLen += buffLen;
    return RET_OK;
}


/*
 *@brief:发送响应报文;
 *@para:struct connection *conn:网络连接
 *
 */
int httpTransferFileData(struct connection *conn){
    int iRet = RET_ERROR;
    if(!conn){
        fprintf(stderr, "[error]get unavailable args in [file]:%s [func]:%s [line]:%d\n", __FILE__,__func__,__LINE__);
        return iRet;
    }
    struct httpOpt *opt = &(conn->httpopt);
#define MAX_HTTP_SEND_BUF_SIZE 4096
    char buf[MAX_HTTP_SEND_BUF_SIZE] = { '\0' };
    size_t leftDataLen = 0, readRoomLen, dataLen = 0;
    size_t currDataLen = strlen((char *)conn->sendbuf);
    /*
     *@brief:
     */
    leftDataLen = opt->contentLen - opt->sended;
    if(currDataLen > MAX_HTTP_SEND_BUF_SIZE){
        readRoomLen = 0; //可读新数据的空间长度为0;
    }else{
        readRoomLen =  MAX_HTTP_SEND_BUF_SIZE - currDataLen;
    }
    /*
     *@brief:判断下文件剩余的大小;
     */
    if(readRoomLen > leftDataLen){
        readRoomLen = leftDataLen;
    }
    if( readRoomLen > 0){
        dataLen = fread(buf, 1, readRoomLen, opt->fp);
        if(dataLen > 0){
            //这边会判断发送缓冲区够不够 不够的话要进行扩充;
            iRet = bufferedFileData(conn, buf, dataLen);
            if(iRet == RET_ERROR){
                goto failed;
            }
            opt->sended += dataLen;
        }
    }
    /*
     *@brief:累计发送的字节数>= 文本内容;
     */
    if(opt->sended >= opt->contentLen){
        if(opt->keepAlive == false){
            iRet = httpFreeProtoDataFile(opt);
            if(iRet == RET_ERROR){
                goto failed;
            }
        }
    }
    conn->state = SEND_RESPONSE;
    return RET_OK;
failed:
    conn->state = CONNECTION_CLOSE; 
    return RET_ERROR;
}

/*@brief:释放文件资源
 *@para:struct httpOpt *opt: 
 *
 *
 *@return value:
 */
int httpFreeProtoDataFile(struct httpOpt *opt){
    int iRet = RET_ERROR;
    if(!opt){
        fprintf(stderr, "[error]get unavailable args in [func]:%s [file]:%s [line]:%d\n", __func__, __FILE__,__LINE__);
        return iRet;
    }
    if(opt->fp!=NULL){
        fclose(opt->fp);
        memset(opt, 0, sizeof(struct httpOpt));
    }
    return RET_OK;
}


/*
 *@brief:
 *@para:void 
 *
 */
double httpGetLocalTime(){
    double now;
    struct timeval ts;
    gettimeofday(&ts, NULL);
    now = (double)ts.tv_sec + (double)(ts.tv_usec/1000000);
    return now;
}



/*
 *@brief:发送响应状态行和头部
 */
int httpSendResponseLine(struct connection *conn, int statusCode, char * extraHeaders){
    //入参判断;
    int iRet = RET_ERROR;
    if(!conn){
        fprintf(stderr, "[error]get Unavailable args in [file]:%s [func]:%s [line]:%d\n",__FILE__,__func__, __LINE__);
        return iRet;
    }
    int dataLen = 0;
    size_t len = 0;
    char *sendbuf = (char *)conn->sendbuf;
    memset(sendbuf, '\0', conn->bufSize);
    if(extraHeaders){
        len = strlen(extraHeaders);
    }
    dataLen += snprintf(sendbuf,conn->bufSize - dataLen, "HTTP/1.1 %d %s\r\nServer: %s\r\n", statusCode, httpStatusMessage(statusCode), "webserver/0.1");
    if(len > 0){ //有其他的头部字段需要加入;
        //判断缓冲区;
        if(len < conn->bufSize -  dataLen){
            dataLen += snprintf(sendbuf + dataLen, conn->bufSize - dataLen, extraHeaders);
        }//否则报错;
    }
    conn->currDataLen += dataLen;
    return dataLen;
}

/*@brief:格式化GMT时间戳;
 *
 */
void httpGmtTimeString(char *timebuf, int buflen, time_t *time){
    strftime(timebuf, buflen, "%a, %d %b %Y %H:%M:%S GMT", gmtime(time));
}

/*
 *@brief:创建etag;
 */
void httpConstructEtag(char *etagbuf, int etaglen, struct stat *indexfile){
#define INT64_FMT "ld"
    snprintf(etagbuf, etaglen, "\"%lx.%" INT64_FMT "\"", (unsigned long)indexfile->st_mtime,(int64_t)indexfile->st_size);
}


/*@brief:状态消息
 *@para:int errorCode:错误码
 */
char * httpStatusMessage(int errorCode){
    switch(errorCode){
    case HTTP_PARTIAL_CODE:
        return "Partial Content";
    case HTTP_MOVED_CODE:
        return "Moved";
    case HTTP_FOUND_CODE:
        return "Found";
    case HTTP_BAD_REQUEST_CODE:
        return "Bad Request";
    case HTTP_UNAUTHORIZED_COEE:
        return "Unauthorized";
    case HTTP_FORBIDDEN_CODE:
        return "Forbidden";
    case HTTP_NOT_FOUND_CODE:
        return "Not Found";
    case HTTP_REQUEST_RANGE_NOT_STATISFIABLE:
        return "Requested Range Not Satisfiable";
    case HTTP_BAD_GATEWAY:
        return "Bad Gateway";
    case HTTP_INTERNAL_SERVER_ERROR:
        return "Internal Server Error!";
    case HTTP_SERVICE_UNAVALIABLE:
        return "Service Unavailable";
    default:
        return "OK";
    }
}

/*@brief:循环遍历HTTP的报文头部, 给定http头部的name, 求出给定的http的值
 *@para: struct httpMessage *hm:http的报文头部 
 *		 const char *name:要获取值的name
 *@return value:
 */
struct dynString *httpGetHttpHeader(struct httpMessage *hm, const char *name){
    /*
     *@循环遍历请求报文中的头部;
     */
    size_t i = 0, len = strlen(name);
    for(i = 0; hm->headerNames[i].len > 0; i++){
        struct dynString *head = &(hm->headerNames[i]), *value = &(hm->headerValues[i]);
        if(head->str != NULL && head->len == len && strncasecmp(value->str, name, len) == 0){
            return value;
        }
    }
    return NULL;
}



